using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace SP
{
    /// <summary>
    /// Preprocessors (i.e. #define)
    /// </summary>
    public class Preprocessor
    {
        public Preprocessor(string name, int min, int max, int val, string desc)
        {
            Name = name;
            Desc = desc;
            mMin = min;
            mMax = max;
            mVal = val;
        }

        public string Name;

        public string Desc;

        public int Value
        {
            get { return mVal; }
            set { if(value <= mMax && value >= mMin) mVal = value; }
        }

        private int mMin, mMax, mVal;
    }

    /// <summary>
    /// Parameters (i.e. texture2D, vec2, vec3, vec4 ...etc)
    /// </summary>
    public interface IParameter
    {
        string Name { get; }

        string Desc { get; }

        // convert the value from/to string
        string Value { get; set; }

        // whether this parameter is editable in UI
        bool Editable { get; set; }
    }

    /// <summary>
    /// Stages of shader fusions
    /// </summary>
    public enum Stage
    {
        Vertex0 = 0x00010001,
        Vertex1 = 0x00010002,
        Vertex2 = 0x00010003,
        Vertex3 = 0x00010004,
        Parallex = 0x00020001,
        AlbedoGloss = 0x00020002,
        Opacity = 0x00020003,
        Illumination = 0x00020004,
        Relfection = 0x00020005,
        Override = 0x00020006,
    }

    /// <summary>
    /// This interface define what a shader feature needed to support.
    /// </summary>
    public interface IFusion
    {
        string Name { get;}

        string Desc { get;}

        List<Preprocessor> Preprocessors {get;}

        List<IParameter> Parameters {get;}

        Stage Stage {get;}

        bool Enabled {get; set;}

        int UVChannelCount { get;}

        void Code(StringBuilder b, string platform);

        Preprocessor GetPreprocessor(string name);

        IParameter GetParameter(string name);
    }

    /// <summary>
    /// IReactor combines several fusions into code files
    /// </summary>
    public interface IReactor
    {
        string Name { get;}

        string[] CodeFiles {get;}

        void InitShader(Shader shader);

        void Generate(Shader shader, string codeFile, StringBuilder b);
    }

    public class ReactorDB
    {
        private ReactorDB()
        {
        }

        public List<IReactor> Reactors = new List<IReactor>();

        public IReactor GetReactorByName(string name)
        {
            foreach (IReactor r in Reactors)
                if (r.Name.Equals(name))
                    return r;

            return null;
        }

        static private ReactorDB _singleton = null;
        static public ReactorDB Singleton
        {
            get
            {
                if (_singleton == null)
                    _singleton = new ReactorDB();

                return _singleton;
            }
        }
    }

    /// <summary>
    /// Shader is composed of IFusion
    /// </summary>
    public class Shader
    {
        public Shader(string name) : this()
        {
            this.Name = name;
        }

        public Shader()
        {
            mFusions = new List<IFusion>();
        }

        [XmlIgnore]
        public List<IFusion> Fusions
        {
            get { return mFusions; }
        }

        [XmlIgnore]
        public int UVChannelCount
        {
            get
            {
                int cnt = 0;

                foreach (IFusion f in mFusions)
                    cnt = Math.Max(cnt, f.UVChannelCount);

                return cnt;
            }
        }

        public ShaderState State
        {
            get { return ShaderState.FromShader(this); }
            set { ShaderState.ToShader(value, this); }
        }

        public IFusion GetFusion(string name)
        {
            foreach (IFusion f in mFusions)
                if (f.Name.Equals(name))
                    return f;

            return null;
        }

        public string Reactor;

        public string Name;

        public bool DepthTest = true;

        public bool Transparent = false;

        public bool TwoSided = false;

        private List<IFusion> mFusions;
    }

}   // namesapce
